﻿using System;
using System.Collections.Generic;
using ViewModelMapper.Extensions;
using System.Linq;
using System.Linq.Expressions;

namespace ViewModelMapper
{
    public abstract class MappedViewModel<T, U> where T : MappedViewModel<T, U>
    {
        public void CopyTo(U model)
        {
            Copy(from => from.MapFrom, to => to.MapTo, (T)this, model, m => m.Direction != Direction.From, model);
        }

        public void CopyFrom(U model)
        {
            Copy(from => from.MapTo, to => to.MapFrom, model, (T)this, m => m.Direction != Direction.To, model);
        }

        public Map<T, U> Map(Expression<Func<T, object>> from)
        {
            return new Map<T, U>(from);
        }

        public abstract IEnumerable<Map<T, U>> Mappings();

        private void Copy<V, W>(
            Func<Map<T, U>, Expression<Func<V, object>>> fromExpressionGetter, 
            Func<Map<T, U>, Expression<Func<W, object>>> toExpressionGetter,
            V copyFrom,
            W copyTo,
            Func<Map<T, U>, bool> directionConstraint,
            U model)
        {
            foreach (var mapping in Mappings().Where(m => directionConstraint(m) && m.Constraint((T)this, model)))
            {
                var destinationProperty = toExpressionGetter(mapping).GetPropertyInfo();

                if (destinationProperty == null)
                {
                    continue;
                }

                var sourceValue = fromExpressionGetter(mapping).Compile().Invoke(copyFrom);

                destinationProperty.SetValue(copyTo, sourceValue, null);
            }
        }
    }
}
